package main

import (
	"fmt"
	"time"
)

//--------------有关Task任务角色的功能---------------

// 定义一个任务类型 Task
type Task struct {
	// 一个Task里面应该有一个具体的业务，业务名称就叫f
	f func() error
}

// 创建一个Task任务
func NewTask(arg_f func() error) *Task {
	t := Task{
		f: arg_f,
	}
	return &t
}

// Task也需要一个执行业务的方法
func (t *Task) Execute() {
	t.f() // 调用任务中已绑定好的业务方法
}

// ---------------有关协程池Pool角色的功能---------------

// 定义一个Pool协程池的类型
type Pool struct {
	// 对外的Task入口 EntryChannel
	EntryChannel chan *Task

	// 内部的Task队列 JobChannel
	JobChannel chan *Task

	// 协程池中最大的worker的数量
	worker_num int
}

// 创建Pool的函数
func NewPool(cap int) *Pool {
	// 创建一个Pool
	p := Pool{
		EntryChannel: make(chan *Task),
		JobChannel:   make(chan *Task),
		worker_num:   cap,
	}

	// 返回Pool个数
	return &p
}

// 协程池创建一个Worker，并且让这个Worker去工作
func (p *Pool) worker(worler_ID int) {
	// 一个worker具体的工作

	// 永久的从JobChannel去取任务
	for task := range p.JobChannel {
		// task就是当前worker从JobChannel中拿到的任务
		// 一旦取到任务，执行这个任务
		task.Execute()
		fmt.Println("worler_ID", worler_ID, "执行完了一个任务")
	}
}

// 让协程池开始工作，启动方法
func (p *Pool) run() {
	// 根据worker_num来创建worker去工作
	for i := 0; i < p.worker_num; i++ {
		// 每个worker都应该是一个goroutine
		go p.worker(i)
	}
	// 从EntryChannel中去取任务，将取到的任务发送给JobChannel
	for task := range p.EntryChannel {
		p.JobChannel <- task
	}
}

// 主函数
func main() {
	// 创建一些任务
	t := NewTask(func() error {
		fmt.Println(time.Now())
		return nil
	})
	// 创建一个Pool协程池
	p := NewPool(4)
	task_num := 0
	// 将这些任务，交给协程池Pool
	go func() {
		for {
			// 不断向p中写入任务t，每个任务就是打印当前的时间
			p.EntryChannel <- t
			task_num += 1
			fmt.Println("当前一共执行了", task_num, "个任务")
		}
	}()

	// 启动pool
	p.run()
}
